home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue64 / WBroker / NewParse.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-12-03  |  17.4 KB  |  660 lines

  1. { *****************************************************
  2.                  NewParse Unit
  3.  
  4.                   Paul Warren
  5.          HomeGrown Software Development
  6.        (c) 1997 Langley British Columbia.
  7.                 (604) 856-6523
  8.          e-mail:  hg_soft@uniserve.com
  9.     Home page: http://users.uniserve.com/~hg_soft
  10.  
  11.     04/26/99 - Added FULLLINECOUNT define so that you can
  12.     select whether to count multiline comments. Default is
  13.     off so that the PasToWeb code will work properly.
  14.  
  15.   ***************************************************** }
  16.  
  17. unit Newparse;
  18. { $DEFINE DEBUG}
  19. { $DEFINE FULLLINECOUNT}
  20.  
  21. interface
  22.  
  23. uses Classes, Consts, SysUtils, Dialogs;
  24.  
  25. type
  26.   TParserClass = class of TCustomParser;
  27.  
  28.   TCustomParser = class
  29.   private
  30.     { private declarations }
  31.     FStream: TStream;
  32.     FOrigin: Longint;
  33.     FBuffer: PChar;
  34.     FBufPtr: PChar;
  35.     FBufEnd: PChar;
  36.     FSourcePtr: PChar;
  37.     FSourceEnd: PChar;
  38.     FTokenPtr: PChar;
  39.     FStringPtr: PChar;
  40.     FSourceLine: Integer;
  41.     FSaveChar: Char;
  42.     FToken: Char;
  43.     procedure ReadBuffer;
  44.     procedure SkipBlanks;
  45.     {$IFDEF Win32}
  46.     procedure Error(const Ident: string); virtual;
  47.     {$ELSE}
  48.     procedure Error(MessageID: Word); virtual;
  49.     {$ENDIF}
  50.     procedure ErrorStr(const Message: string);
  51.   public
  52.     { public declarations }
  53.     constructor Create(Stream: TStream); virtual;
  54.     destructor Destroy; override;
  55.     function NextToken: Char; virtual;
  56.     function TokenString: string; virtual;
  57.     function SourcePos: Longint;
  58.     property Token: Char read FToken;
  59.     property SourceLine: integer read FSourceLine;
  60.   end;
  61.  
  62.   TCSVParser = class(TCustomParser)
  63.   private
  64.     { private declarations }
  65.   public
  66.     { public declarations }
  67.     function TokenString: string; override;
  68.     function NextToken: char; override;
  69.   end;
  70.  
  71.   TTextParser = class(TCustomParser)
  72.   private
  73.     { private declarations }
  74.   public
  75.     { public declarations }
  76.     function NextToken: Char; override;
  77.   end;
  78.  
  79.   TPasParser = class(TTextParser)
  80.   private
  81.     { private declarations }
  82.   public
  83.     { public declarations }
  84.     function NextToken: Char; override;
  85.   end;
  86.  
  87. const
  88.   toComment = Char(5);
  89.  
  90. type
  91.   TEnhPasParser = class(TPasParser)
  92.   private
  93.     { private declarations }
  94.   public
  95.     { public declarations }
  96.     function TokenString: string; override;
  97.     function NextToken: Char; override;
  98.   end;
  99.  
  100. const
  101.   toOpenTag = Char(6);
  102.   toCloseTag = Char(7);
  103.  
  104. type
  105.   THtmlParser = class(TTextParser)
  106.   private
  107.     { private declarations }
  108.   public
  109.     { public declarations }
  110.     function TokenString: string; override;
  111.     function NextToken: Char; override;
  112.   end;
  113.  
  114. function ParseIt(var ParseStr: string; Delimiter: string): string;
  115.  
  116. var
  117.   Log: TextFile;
  118.  
  119. implementation
  120.  
  121. function ParseIt(var ParseStr: string; Delimiter: string): string;
  122. var
  123.   Len: integer;
  124. begin
  125.   Result := '';
  126.   if Length(ParseStr) > 0 then // if there is something to parse...
  127.   begin
  128.     if Pos(Delimiter, ParseStr) = 1 then  // if it begins with a delimiter...
  129.       System.Delete(ParseStr, 1, Length(Delimiter)); // then delete it
  130.     if Pos(Delimiter, ParseStr) <> 0 then  // if there is a delimiter...
  131.     begin
  132.       // copy up to it
  133.       Len := Pos(Delimiter, ParseStr);
  134.       Result := System.Copy(ParseStr, 1, Len-1);
  135.     end else
  136.     begin
  137.       // else copy all remaining string
  138.       Len := Length(ParseStr);
  139.       Result := System.Copy(ParseStr, 1, Len);
  140.     end;
  141.     // delete what we copied
  142.     System.Delete(ParseStr, 1, Len);
  143.   end;
  144. end;
  145.  
  146. const
  147.   ParseBufSize: integer = 4096;
  148.  
  149. { TCustomParser }
  150. constructor TCustomParser.Create(Stream: TStream);
  151. begin
  152.   FStream := Stream;
  153.   GetMem(FBuffer, ParseBufSize);
  154.   FBuffer[0] := #0;
  155.   FBufPtr := FBuffer;
  156.   FBufEnd := FBuffer + ParseBufSize;
  157.   FSourcePtr := FBuffer;
  158.   FSourceEnd := FBuffer;
  159.   FTokenPtr := FBuffer;
  160.   FSourceLine := 1;
  161.   {$IFDEF DEBUG}
  162.   writeln(log,'');
  163.   writeln(log, 'FBuffer FBufPtr FSrcPtr   FSrcEnd FBufEnd Pos Occured');
  164.   writeln(log,'');
  165.   writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^, ' ', LongInt(FSourceEnd), ' ', LongInt(FBufEnd), ' ',FStream.Position,' on create');
  166.   {$ENDIF}
  167.   NextToken;
  168. end;
  169.  
  170. destructor TCustomParser.Destroy;
  171. begin
  172.   if FBuffer <> nil then
  173.   begin
  174.     FStream.Seek(Longint(FTokenPtr) - Longint(FSourceEnd), 1);
  175.     FreeMem(FBuffer, ParseBufSize);
  176.   end;
  177. end;
  178.  
  179. procedure TCustomParser.ReadBuffer;
  180. var
  181.   Count: Integer;
  182. begin
  183.   try
  184.     Inc(FOrigin, FSourcePtr - FBuffer);
  185.     FSourceEnd[0] := FSaveChar;
  186.   {$IFDEF DEBUG}
  187.     writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^,' ', LongInt(FSourceEnd),' ',LongInt(FBufEnd), ' ',FStream.Position, ' before read');
  188.   {$ENDIF}
  189.     Count := FBufPtr - FSourcePtr;
  190.     if Count <> 0 then Move(FSourcePtr[0], FBuffer[0], Count);
  191.     FBufPtr := FBuffer + Count;
  192.     Inc(FBufPtr, FStream.Read(FBufPtr[0], FBufEnd - FBufPtr));
  193.   {$IFDEF DEBUG}
  194.     writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^, ' ', LongInt(FSourceEnd), ' ', LongInt(FBufEnd), ' ',FStream.Position, ' after read');
  195.   {$ENDIF}
  196.     FSourcePtr := FBuffer;
  197.     FSourceEnd := FBufPtr;
  198.     if FSourceEnd = FBufEnd then
  199.     begin
  200.       FSourceEnd := LineStart(FBuffer, FSourceEnd - 1);
  201.       if FSourceEnd = FBuffer then Error(SLineTooLong);
  202.     end;
  203.     FSaveChar := FSourceEnd[0];
  204.     FSourceEnd[0] := #0;
  205.   except
  206.     on EStreamError do
  207.       MessageDlg('FSourcePtr^ = '+FSourcePtr^,mtError,
  208.         [mbOK],0);
  209.     on EAccessViolation do
  210.       MessageDlg('FSourcePtr^ = '+FSourcePtr^,mtError,
  211.         [mbOK],0);
  212.   end;
  213. end;
  214.  
  215. function TCustomParser.NextToken: Char;
  216. begin
  217.   FToken := FSourcePtr^;
  218.   if FToken <> toEOF then Inc(FSourcePtr);
  219.   Result := FToken;
  220. end;
  221.  
  222. procedure TCustomParser.SkipBlanks;
  223. begin
  224.   while True do
  225.   begin
  226.     case FSourcePtr^ of
  227.       #0:
  228.         begin
  229.           ReadBuffer;
  230.           if FSourcePtr^ = #0 then Exit;
  231.           Continue;
  232.         end;
  233.       #10:
  234.         Inc(FSourceLine);
  235.       #33..#255:
  236.         Exit;
  237.     end;
  238.     Inc(FSourcePtr);
  239.   end;
  240. end;
  241.  
  242. function TCustomParser.TokenString: string;
  243. var
  244.   L: Integer;
  245. begin
  246.   if (FToken = toString) then
  247.     L := FStringPtr - FTokenPtr else
  248.     L := FSourcePtr - FTokenPtr;
  249.   {$IFDEF Win32}
  250.   SetString(Result, FTokenPtr, L);
  251.   {$ELSE}
  252.   if L > 255 then L := 255;
  253.   Result[0] := Char(L);
  254.   {$ENDIF}
  255.   Move(FTokenPtr[0], Result[1], L);
  256. end;
  257.  
  258. {$IFDEF Win32}
  259. procedure TCustomParser.Error(const Ident: string);
  260. begin
  261.   ErrorStr(Ident);
  262. end;
  263.  
  264. procedure TCustomParser.ErrorStr(const Message: string);
  265. begin
  266.   raise EParserError.CreateFmt(SParseError, [Message, FSourceLine]);
  267. end;
  268. {$ELSE}
  269. procedure TCustomParser.Error(MessageID: Word);
  270. begin
  271.   ErrorStr(LoadStr(MessageID));
  272. end;
  273.  
  274. procedure TCustomParser.ErrorStr(const Message: string);
  275. begin
  276.   raise EParserError.Create(FmtLoadStr(SParseError, [Message, FSourceLine]));
  277. end;
  278. {$ENDIF}
  279.  
  280. function TCustomParser.SourcePos: Longint;
  281. begin
  282.   Result := FOrigin + (FTokenPtr - FBuffer);
  283. end;
  284.  
  285. { TCSVParser }
  286. function TCSVParser.TokenString: string;
  287. var
  288.   L: Integer;
  289. begin
  290.   if (FToken = toSymbol) then
  291.     L := FStringPtr - FTokenPtr else
  292.     L := FSourcePtr - FTokenPtr;
  293.   {$IFDEF Win32}
  294.   SetString(Result, FTokenPtr, L);
  295.   {$ELSE}
  296.   if L > 255 then L := 255;
  297.   Result[0] := Char(L);
  298.   {$ENDIF}
  299.   Move(FTokenPtr[0], Result[1], L);
  300. end;
  301.  
  302. function TCSVParser.NextToken: Char;
  303. begin
  304.   SkipBlanks;
  305.   FTokenPtr := FSourcePtr;
  306.   case FSourcePtr^ of
  307.     'A'..'Z', 'a'..'z', '_':
  308.       begin
  309.         Inc(FSourcePtr);
  310.         FStringPtr := FSourcePtr;
  311.         while true do
  312.         begin
  313.           case FSourcePtr^ of
  314.             ',': Break;
  315.             #0: Break;
  316.           end;
  317.           FStringPtr^ := FSourcePtr^;
  318.           Inc(FStringPtr);
  319.           Inc(FSourcePtr);
  320.         end;
  321.         FToken := toSymbol;
  322.         Result := FToken;
  323.       end;
  324.     '-', '0'..'9':
  325.       begin
  326.         Inc(FSourcePtr);
  327.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  328.         FToken := toInteger;
  329.         Result := FToken;
  330.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  331.         begin
  332.           Inc(FSourcePtr);
  333.           FToken := toFloat;
  334.           Result := FToken;
  335.         end;
  336.       end;
  337.     else Result := inherited NextToken;
  338.   end;
  339. end;
  340.  
  341. { TTextParser }
  342. function TTextParser.NextToken: Char;
  343. begin
  344.   SkipBlanks;
  345.   FTokenPtr := FSourcePtr;
  346.   case FSourcePtr^ of
  347.     'A'..'Z', 'a'..'z', '_':
  348.       begin
  349.         Inc(FSourcePtr);
  350.         while True do
  351.           case FSourcePtr^ of
  352.             'A'..'Z', 'a'..'z', '0'..'9', '_': Inc(FSourcePtr);
  353.             '''': begin  { apostrophies }
  354.                 if (FSourcePtr+1)^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] then Inc(FSourcePtr)
  355.                 else Break;
  356.               end;
  357.             '-': begin  { hyphenated words }
  358.                 if (FSourcePtr+1)^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] then Inc(FSourcePtr)
  359.                 else Break;
  360.               end;
  361.             else Break;
  362.           end;
  363.         FToken := toSymbol;
  364.         Result := FToken;
  365.       end;
  366.     '-', '0'..'9':
  367.       begin
  368.         Inc(FSourcePtr);
  369.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  370.         FToken := toInteger;
  371.         Result := FToken;
  372.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  373.         begin
  374.           Inc(FSourcePtr);
  375.           FToken := toFloat;
  376.           Result := FToken;
  377.         end;
  378.       end;
  379.     else Result := inherited NextToken;
  380.   end;
  381. end;
  382.  
  383. { TPasParser }
  384. function TPasParser.NextToken: Char;
  385. var
  386.   I: integer;
  387. begin
  388.   SkipBlanks;
  389.   FTokenPtr := FSourcePtr;
  390.   case FSourcePtr^ of
  391.     'A'..'Z', 'a'..'z', '_':
  392.       begin
  393.         Inc(FSourcePtr);
  394.         while FSourcePtr^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] do Inc(FSourcePtr);
  395.         FToken := toSymbol;
  396.         Result := FToken;
  397.       end;
  398.     '#', '''':
  399.       begin
  400.         FStringPtr := FSourcePtr;
  401.         while True do
  402.           case FSourcePtr^ of
  403.             '#':
  404.               begin
  405.                 Inc(FSourcePtr);
  406.                 I := 0;
  407.                 while FSourcePtr^ in ['0'..'9'] do
  408.                 begin
  409.                   I := I * 10 + (Ord(FSourcePtr^) - Ord('0'));
  410.                   Inc(FSourcePtr);
  411.                 end;
  412.                 FStringPtr^ := Chr(I);
  413.                 Inc(FStringPtr);
  414.               end;
  415.             '''':
  416.               begin
  417.                 Inc(FSourcePtr);
  418.                 while True do
  419.                 begin
  420.                   case FSourcePtr^ of
  421.                     #0, #10, #13:
  422.                       Error(SInvalidString);
  423.                     '''':
  424.                       begin
  425.                         Inc(FSourcePtr);
  426.                         if FSourcePtr^ <> '''' then Break;
  427.                       end;
  428.                   end;
  429.                   FStringPtr^ := FSourcePtr^;
  430.                   Inc(FStringPtr);
  431.                   Inc(FSourcePtr);
  432.                 end;
  433.               end;
  434.           else
  435.             Break;
  436.           end;
  437.         FToken := toString;
  438.         Result := FToken;
  439.       end;
  440.     '$':
  441.       begin
  442.         FToken := FSourcePtr^;  { assume NOT an integer }
  443.         Result := FToken;
  444.         Inc(FSourcePtr);
  445.         while true do
  446.         begin
  447.           case FSourcePtr^ of
  448.             '0'..'9', 'A'..'F', 'a'..'f': Inc(FSourcePtr);
  449.             else Break;
  450.           end;
  451.           FToken := toInteger;
  452.           Result := FToken;
  453.         end;
  454.       end;
  455.   (*  '-', '0'..'9':
  456.       begin
  457.         Inc(FSourcePtr);
  458.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  459.         FToken := toInteger;
  460.         Result := FToken;
  461.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  462.         begin
  463.           Inc(FSourcePtr);
  464.           FToken := toFloat;
  465.           Result := FToken;
  466.         end;
  467.       end;  *)
  468.     else Result := inherited NextToken;
  469.   end;
  470. end;
  471.  
  472. { TEnhPasParser }
  473. function TEnhPasParser.TokenString: string;
  474. var
  475.   L: Integer;
  476. begin
  477.   if (FToken = toString) or (FToken = toComment) then
  478.     L := FStringPtr - FTokenPtr else
  479.     L := FSourcePtr - FTokenPtr;
  480.   {$IFDEF Win32}
  481.   SetString(Result, FTokenPtr, L);
  482.   {$ELSE}
  483.   if L > 255 then L := 255;
  484.   Result[0] := Char(L);
  485.   {$ENDIF}
  486.   Move(FTokenPtr[0], Result[1], L);
  487. end;
  488.  
  489. function TEnhPasParser.NextToken: Char;
  490. begin
  491.   SkipBlanks;
  492.   FTokenPtr := FSourcePtr;
  493.   case FSourcePtr^ of
  494.     '{':
  495.       begin { comment or compiler directive... }
  496.         FStringPtr := FSourcePtr;
  497.         Inc(FSourcePtr);  { check next char... }
  498.         while true do
  499.         begin
  500.           case FSourcePtr^ of
  501.             #0: begin
  502.               ReadBuffer;
  503.               FStringPtr := FSourcePtr;
  504.               if FSourcePtr^ = #0 then Break;
  505.               {$IFDEF DEBUG}
  506.               writeln(Log, 'in comment');
  507.               {$ENDIF}
  508.             end;
  509.             {$IFDEF FULLLINECOUNT}
  510.             #10: Inc(FSourceLine);
  511.             {$ENDIF}
  512.             '}':
  513.               begin
  514.                 Inc(FSourcePtr);
  515.                 Break;      { end comment... }
  516.               end;
  517.           end;
  518.           FStringPtr^ := FSourcePtr^;
  519.           Inc(FStringPtr);
  520.           Inc(FSourcePtr);
  521.         end;
  522.         FToken := toComment;
  523.         Result := FToken;
  524.       end;
  525.     '(', '/':  { possible comment or compiler directive... }
  526.       begin
  527.         FToken := FSourcePtr^; { assume NOT a comment }
  528.         Result := FToken;
  529.         FStringPtr := FSourcePtr;
  530.         Inc(FSourcePtr);  { check next char... }
  531.         case FSourcePtr^ of
  532.           '*':  { is a comment }
  533.             begin
  534.               Inc(FSourcePtr);  { check next char... }
  535.               while True do
  536.               begin
  537.                 case FSourcePtr^ of
  538.                   #0: begin
  539.                     ReadBuffer;
  540.                     FStringPtr := FSourcePtr;
  541.                     if FSourcePtr^ = #0 then Break;
  542.                     {$IFDEF DEBUG}
  543.                     writeln(Log, 'in comment');
  544.                     {$ENDIF}
  545.                   end;
  546.                   {$IFDEF FULLLINECOUNT}
  547.                   #10: Inc(FSourceLine);
  548.                   {$ENDIF}
  549.                   '*':
  550.                     begin
  551.                       Inc(FSourcePtr);
  552.                       if FSourcePtr^ = ')' then
  553.                       begin
  554.                         Inc(FSourcePtr);
  555.                         Break; { end of comment }
  556.                       end;
  557.                     end;
  558.                 end;
  559.                 FStringPtr^ := FSourcePtr^;
  560.                 Inc(FStringPtr);
  561.                 Inc(FSourcePtr);
  562.               end;
  563.               FToken := toComment;
  564.               Result := FToken;
  565.             end;
  566.           '/':  { is a comment }
  567.             begin
  568.               Inc(FSourcePtr);
  569.               while (FSourcePtr^ <> #13) do  { end of line, hence comment }
  570.               begin
  571.                 FStringPtr^ := FSourcePtr^;
  572.                 Inc(FStringPtr);
  573.                 Inc(FSourcePtr);
  574.               end;
  575.               FToken := toComment;
  576.               Result := FToken;
  577.             end;
  578.         end;
  579.       end;
  580.     else Result := inherited NextToken;
  581.   end;
  582. end;
  583.  
  584. { THtmlParser }
  585. function THtmlParser.TokenString: string;
  586. var
  587.   L: Integer;
  588. begin
  589.   if (FToken = toString) or (FToken = toOpenTag)
  590.     or (FToken = toCloseTag) then
  591.       L := FStringPtr - FTokenPtr else
  592.       L := FSourcePtr - FTokenPtr;
  593.   {$IFDEF Win32}
  594.   SetString(Result, FTokenPtr, L);
  595.   {$ELSE}
  596.   if L > 255 then L := 255;
  597.   Result[0] := Char(L);
  598.   {$ENDIF}
  599.   Move(FTokenPtr[0], Result[1], L);
  600. end;
  601.  
  602. function THtmlParser.NextToken: Char;
  603. begin
  604.   SkipBlanks;
  605.   FTokenPtr := FSourcePtr;
  606.   case FSourcePtr^ of
  607.     '<':   { is a tag }
  608.     begin
  609.       FStringPtr := FSourcePtr;
  610.       Inc(FSourcePtr);
  611.       case FSourcePtr^ of
  612.         '/':  { is an 'close' tag }
  613.           begin
  614.             Inc(FSourcePtr);
  615.             while true do
  616.             begin
  617.               case FSourcePtr^ of
  618.                 #0: begin
  619.                   ReadBuffer;
  620.                   FStringPtr := FSourcePtr;
  621.                   if FSourcePtr^ = #0 then Break;
  622.                 end;
  623.                 '>': begin
  624.                   Inc(FSourcePtr);
  625.                   Break; { end of tag }
  626.                 end;
  627.               end; {case}
  628.               FStringPtr^ := FSourcePtr^;
  629.               Inc(FStringPtr);
  630.               Inc(FSourcePtr);
  631.             end;
  632.             FToken := toCloseTag;
  633.             Result := FToken;
  634.           end;
  635.         else
  636.           begin
  637.             while true do
  638.             begin
  639.               case FSourcePtr^ of
  640.                 #0: begin
  641.                   ReadBuffer;
  642.                   FStringPtr := FSourcePtr;
  643.                   if FSourcePtr^ = #0 then Break;
  644.                 end;
  645.                 '>': begin
  646.                   Inc(FSourcePtr);
  647.                   Break; { end of tag }
  648.                 end;
  649.               end; {case}
  650.               FStringPtr^ := FSourcePtr^;
  651.               Inc(FStringPtr);
  652.               Inc(FSourcePtr);
  653.             end;
  654.             FToken := toOpenTag;
  655.             Result := FToken;
  656.           end;
  657.       end; {case}
  658.     end;
  659.     else Result := inherited NextToken;
  660.   end;
  661. end;
  662.  
  663. {$IFDEF DEBUG}
  664. initialization
  665.   AssignFile(Log, 'debug.log');
  666.   Rewrite(Log);
  667. finalization
  668.   CloseFile(Log);
  669. {$ENDIF}
  670. end.
  671.